package com.ys.多线程.p4线程同步生产者消费者模式;

/**
 * 描述: 店员是一个共享资源,可以为生产者和消费者服务,但是不能同时服务两者
 *
 *
 * @author CodeYang
 * @version 2021/1/5 19:49
 */
public class Clerk {

    /**
     * 模拟餐台:-1 表示餐台默认为空,- 生产者-生产,消费者 wait 等待
     */
    private int product = -1;


    /**
     * 消费者调用该方法,从餐台拿走产品
     * @return
     */
    public int getProduct() throws Exception {
        // 注意: wait 和 notifyAll 方法必须配合synchronized关键字一起使用,否则会抛出 IllegalMonitorStateException(非法监控状态异常)

        synchronized (this){
            // 表示:餐台为空 ,登台 生产者 生产产品
            while (this.product == -1){
                wait();
            }
            // 餐台不为空 ,拿走产品, 餐台又为空
            int tempProduct = product;
            product = -1;
            // 唤醒所有线程.唤醒 生产者线程
            notifyAll();
            // 这里方法不结束, 其他线程还是在moniotr状态中,只有整个 方法结束,其他的线程才被唤醒
            System.out.println("消费者成功消费了"+tempProduct+"个产品");
            return tempProduct;
        }

    }

    /**
     * 生产者调用该方法,生成产品到餐台
     * @param product
     */
    public void setProduct(int product) throws  Exception{

        synchronized (this){
            // 证明餐台非空,不需要生产,进入等待通知状态
            while (this.product !=-1){
                wait();
            }
            // 证明餐台已经空,需要生产
            // 生产者将产品放入餐台
            this.product = product;
            // 通知消费者拿走产品
            notifyAll();
            // notifyAll 执行之后将 消费者线程唤醒到 montior 状态,
            System.out.println("生产者生产了"+this.product+"个产品");

        }
        // 唤醒后,当执行结束 synchronized 同步块,之后才把 生产者线程 唤醒到Running 状态

        // 如果使用的 synchronized 修饰方法, 只有整个方法结束时才会把 生产者线程唤醒到 Running 状态
        // 如果使用 synchronized 去锁方法就没有montior 而是在方法结束时 直接唤醒到Running状态
        // 如果 生产者已经进入临界区,但是没有离开临界区,如果直接去调用消费的的会直接进入monitor
        // 本质原因: 因为 同步代码块的字节码时 monitor 指令块实现的 所以会进入 monitor状态

    }
}
